<template>
    <a-form
        ref="form"
        :model="model"
        :wrapper-col-props="{ flex: 1 }"
        :label-col-props="{ flex: labelWidth }"
        :class="[search && 'search-form']"
        :scroll-to-first-error="true"
        v-bind="$attrs"
    >
        <a-row :gutter="gutter">
            <template
                v-for="(item, index) in fields"
                :key="item.field || index"
            >
                <a-col
                    v-if="isRenderField(item)"
                    v-show="index < expandNumber || expand"
                    :span="item.span ?? span"
                >
                    <FormElement
                        :fieldItem="item"
                        :model="model"
                        :getField="getField"
                        :colon="colon"
                    >
                        <template
                            v-for="(_, name) in $slots"
                            v-slot:[name]
                        >
                            <slot :name="name"></slot>
                        </template>
                    </FormElement>
                </a-col>
            </template>
            <!-- 这下面这一块儿是搜索表单用的 -->
            <SearchButtonGroup
                v-if="search"
                v-model:expand="expand"
                :expandNumber="expandNumber"
                :search="search"
                :fields="fields"
            />
        </a-row>
    </a-form>
</template>

<script setup lang="ts">
import type { FormProps } from './types.d'
import { cloneDeep } from 'lodash-es'
import { useTemplateRef } from 'vue'
import FormElement from './FormElement.vue'
import SearchButtonGroup from './SearchButtonGroup.vue'
import { FormInstance as AcroFormInstance } from '@arco-design/web-vue'
const {
    model,
    fields,
    initModel,
    span = 8,
    gutter = 20,
    colon = false,
    expandNumber = Infinity,
    labelWidth = '90px',
    getAllFieldValue = false
} = defineProps<FormProps>()

// 展开收起
const expand = defineModel<boolean>('expand', { default: false, required: false })

/**
 * [表单 实例]
 */
const formElement = useTemplateRef<AcroFormInstance>('form')

/**
 * 获取某一个表单项的具体配置
 */
const getField = (name: string) => fields.find(item => item.field === name) as FormFields[0]

/**
 * 决定表单项是否渲染
 */
function isRenderField(current: FormFields[0]): boolean {
    const isRender = current.renderDepend?.({ model, getField }) ?? true
    current._isRender = isRender
    return isRender
}
/**
 * 获取表单字段
 *
 * @returns 返回一个包含所有需要渲染的表单字段的字符串数组
 */
function getModelFields() {
    const modelFields: string[] = []
    fields.forEach(item => {
        const { _isRender, field } = item
        if ((_isRender && !getAllFieldValue) || getAllFieldValue) {
            Array.isArray(field) ? modelFields.push(...field) : modelFields.push(field)
        }
    })
    return modelFields
}
/**
 *  表单暴露出去的方法
 */
const expose = {
    // 获取表单某一项配置
    getField,

    // 校验表单
    validateField: (name: string | string[], callback?: () => undefined | Record<string, any>) =>
        formElement.value?.validateField(name, callback),

    // 重置表单
    reset: (name?: string | string[]) => {
        formElement.value?.resetFields(name)
        initModel && Object.assign(model, cloneDeep(initModel))
    },

    // 获取表单值
    async getFormValue<T = Record<string, any>>(): Promise<T> {
        const modelData: T = {} as T
        const errors = await formElement.value?.validate()
        if (!errors) {
            const modelFields = getModelFields()
            modelFields.forEach(field => {
                modelData[field] = cloneDeep(model[field])
            })
            return modelData
        } else {
            return Promise.reject(errors)
        }
    }
}

defineExpose(expose)
</script>

<style lang="less" scoped>
.search-form {
    :deep(.arco-row) {
        gap: 15px 0;

        .arco-form-item {
            margin-bottom: 0;
        }
    }
}
</style>
